package com.zhou.common.mybatis.template.shiro.zhou.config.redis.cash;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * CacheConfiguration
 *
 * @author zhouxianfeng
 * @date 2021-4-9
 */
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
public class CacheConfiguration {

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();

        config = config.serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(genericFastJsonRedisSerializer));
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory, RedisCacheConfiguration defaultCacheConfig, StringRedisTemplate redisTemplate) {

        Map<String, RedisCacheConfiguration> initialCacheConfiguration = new LinkedHashMap<String, RedisCacheConfiguration>() {
            {
                this.put("demoCache", defaultCacheConfig.entryTtl(Duration.ofMinutes(5)));
            }
        };

        return new CustomRedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory), defaultCacheConfig,
                initialCacheConfiguration, redisTemplate);
    }

    class CustomRedisCacheManager extends RedisCacheManager {

        private RedisCacheWriter myCacheWriter;
        private RedisTemplate redisTemplate;

        public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
            super(cacheWriter, defaultCacheConfiguration);
            this.myCacheWriter = cacheWriter;
        }

        public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
                                       Map<String, RedisCacheConfiguration> initialCacheConfigurations, RedisTemplate redisTemplate) {
            super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, true);
            this.myCacheWriter = cacheWriter;
            this.redisTemplate = redisTemplate;
        }

        @Override
        protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
            String[] array = name.split("#");
            name = array[0];
            if (array.length > 1) { // 解析TTL
                cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(array[1]))); // 秒
            }
            return new MyRedisCache(name, myCacheWriter, cacheConfig);
        }
    }

}
